﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DTO = Microsoft.MSE.Catalog.Contracts;
using DataAccess = Microsoft.MSE.Catalog.Module.Data;
using System.Data.SqlClient;
using System.Xml;
using WCF = System.ServiceModel.Channels;
using WSDL = System.Web.Services.Description;
using System.Xml.Schema;
using System.Globalization;
using System.IO;
using System.ServiceModel;
using System.Data;

namespace Microsoft.MSE.Catalog.Module.Business
{
    public class EndpointManager
    {
        public static NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;

        #region Endpoint related functions

        public static Guid CreateEndpoint(DTO.Endpoint endpoint, SqlTransaction txn, SqlConnection conn)
        {
            //Note: No change tracking, a new endpoint has not been assigned to a runtime
            return DataAccess.Endpoint.CreateEndpoint(endpoint, txn, conn);
        }

        public static DTO.Endpoint UpdateEndpoint(DTO.Endpoint endpoint)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {
                    //change tracking
                    DTO.DBEventContext eventContext = ChangeTrackingHelper.GetEventContextForEndpointUpdate(endpoint, txn, conn);

                    //Calling Data Access layer
                    endpoint = DataAccess.Endpoint.UpdateEndpoint(endpoint, txn, conn);

                    // Update any UDDI associations 
                    eventContext.EventData.ForEach(item => { PublishEndPointToUddi(endpoint.ID, item.RuntimeServerId, false, txn, conn); });

                    //create change record
                    DataAccess.RepositoryChange.Save(eventContext, txn, conn);

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return endpoint;
        }

        public static void DeleteEndpoint(Guid ID)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {
                    //Note: No change tracking, cannot delete an endpoint if it is used by a runtime
                    DataAccess.Endpoint.DeleteEndpoint(ID, txn, conn);

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here
        }

        public static void AssignEndpointToRuntimeServer(Guid endpointID, Guid runtimeServerID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.Endpoint.AssignEndpointToRuntimeServer(endpointID, runtimeServerID, txn, conn);

            // Time to publish Endpoint to UDDI 
            EndpointManager.PublishEndPointToUddi(endpointID, runtimeServerID, false, txn, conn);

            //create change record
            DTO.DBEventContext eventContext = ChangeTrackingHelper.GetEventContextForRuntimeServerAssignEndpoint(runtimeServerID, endpointID, txn, conn);
            Data.RepositoryChange.Save(eventContext, txn, conn);
        }

        public static DTO.EndpointList ListEndpointsForRuntimeServerID(Guid runtimeServerID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Endpoint.ListEndpointsForRuntimeServerID(runtimeServerID, txn, conn);
        }

        public static DTO.EndpointList ListEndpoints(SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Endpoint.ListEndpoints(txn, conn);
        }

        public static DTO.EndpointList ListEndpointsForPolicyID(Guid policyID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Endpoint.ListEndpointsForPolicyID(policyID, txn, conn);
        }

        public static DTO.EndpointList ListEndpointsForBindingID(Guid bindingID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Endpoint.ListEndpointsForBindingID(bindingID, txn, conn);
        }

        public static DTO.Endpoint GetEndpoint(Guid ID, Guid RuntimeServerID, bool provideWSDL, SqlTransaction txn, SqlConnection conn)
        {
            if (RuntimeServerID == Guid.Empty) provideWSDL = false;


            DTO.Endpoint endpoint = DataAccess.Endpoint.GetEndpoint(ID, RuntimeServerID, provideWSDL, txn, conn);

            endpoint.PubOpVersionsList = ListPublishedOperationsForEndpoint(ID, txn, conn);
            endpoint.AssocOpVersionsList = ListAssociatedOperationsForEndpoint(ID, txn, conn);

            System.ServiceModel.Channels.Binding binding = BindingHelper.DeserializeBinding(endpoint.Binding.BindingConfig);

            // Provide WSDL only if demanded
            if (provideWSDL)
            {
                DTO.RuntimeServer rs = DataAccess.RuntimeServer.GetRuntimeServer(RuntimeServerID, provideWSDL, txn, conn);

                if (endpoint.Port != 0)
                    rs.HostName += ":" + Convert.ToString(endpoint.Port);

                endpoint.Address = rs.HostName + @"/" + endpoint.Path;

                //endpoint.WSDL = GenerateWSDLForEndpoint(endpoint, endpoint.Address, txn, conn);
                endpoint.WSDL = CreateWSDLFromEndpointMetadata(endpoint, txn, conn);
            }
            else
            {
                // Just set the address to the path to maintain compatibility
                endpoint.Address = endpoint.Path;
            }

            endpoint.UddiAssociations = ListEndpointUddiAssociation(ID, txn, conn);

            return endpoint;
        }

        public static DTO.Endpoint GetEndpoint(Guid ID, Guid RuntimeServerID, string hostName, bool provideWSDL, SqlTransaction txn, SqlConnection conn)
        {
            DTO.Endpoint endpoint = DataAccess.Endpoint.GetEndpoint(ID, RuntimeServerID, provideWSDL, txn, conn);

            endpoint.PubOpVersionsList = ListPublishedOperationsForEndpoint(ID, txn, conn);
            endpoint.AssocOpVersionsList = ListAssociatedOperationsForEndpoint(ID, txn, conn);

            System.ServiceModel.Channels.Binding binding = BindingHelper.DeserializeBinding(endpoint.Binding.BindingConfig);

            // Provide WSDL only if demanded
            if (provideWSDL)
            {
                if (endpoint.Port != 0)
                    hostName += ":" + Convert.ToString(endpoint.Port);

                endpoint.Address = hostName + @"/" + endpoint.Path;

                //endpoint.WSDL = GenerateWSDLForEndpoint(endpoint, endpoint.Address, txn, conn);
                endpoint.WSDL = CreateWSDLFromEndpointMetadata(endpoint, txn, conn);
            }
            else
            {
                // Just set the address to the path to maintain compatibility
                endpoint.Address = endpoint.Path;
            }

            endpoint.UddiAssociations = ListEndpointUddiAssociation(ID, txn, conn);

            return endpoint;
        }

        public static DTO.OperationVersionList ListAssociatedOperationsForEndpoint(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Endpoint.ListAssociatedOperationsForEndpoint(ID, txn, conn);
        }

        public static DTO.EndpointList ListAssociatedEndpointsForOperation(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Endpoint.ListAssociatedEndpointsForOperation(ID, txn, conn);
        }

        public static DTO.OperationVersionList ListPublishedOperationsForEndpoint(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Endpoint.ListPublishedOperationsForEndpoint(ID, txn, conn);
        }

        public static void RemoveEndpointFromRuntimeServer(Guid endpointID, Guid runtimeServerID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.Endpoint.RemoveEndpointFromRuntimeServer(endpointID, runtimeServerID, txn, conn);

            // Time to remove Endpoint to UDDI 
            RemoveEndPointFromUddi(endpointID, runtimeServerID, txn, conn);

            //create change record
            DTO.DBEventContext eventContext = ChangeTrackingHelper.GetEventContextForRuntimeServerRemoveEndpoint(runtimeServerID, endpointID, txn, conn);
            DataAccess.RepositoryChange.Save(eventContext, txn, conn);
        }

        public static void RemoveEndpointsFromRuntimeServer(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            //change tracking
            DTO.DBEventContext eventContext = ChangeTrackingHelper.GetEventContextForRuntimeServerRemoveEndpoints(ID, txn, conn);

            DataAccess.Endpoint.RemoveEndpointsFromRuntimeServer(ID, txn, conn);

            //create change record
            DataAccess.RepositoryChange.Save(eventContext, txn, conn);
        }

        #endregion

        #region Endpoint Group related functions

        public static List<DTO.EndpointListElement> ListEndpointsByGroup(Guid entityGroupID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Endpoint.ListEndpointsByGroup(entityGroupID, txn, conn);
        }

        public static DTO.EndpointGroup CreateEndpointGroup(DTO.EndpointGroup endpointGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {
                    endpointGroup.ID = DataAccess.Entity.CreateEntityGroup(endpointGroup.Name, endpointGroup.Description, 2, txn, conn);

                    if (endpointGroup.Endpoints != null)
                    {
                        foreach (DTO.EndpointListElement endpoint in endpointGroup.Endpoints)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(endpointGroup.ID, endpoint.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return endpointGroup;
        }

        public static DTO.EndpointGroup UpdateEndpointGroup(DTO.EndpointGroup endpointGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                    DataAccess.Entity.UpdateEntityGroup(endpointGroup.ID, endpointGroup.Name, endpointGroup.Description, txn, conn);


                    List<DTO.EndpointListElement> oldList = EndpointManager.ListEndpointsByGroup(endpointGroup.ID, txn, conn);
                    /// Delete all items from the old list
                    if (oldList != null)
                    {
                        foreach (DTO.EndpointListElement oldListElement in oldList)
                        {
                            DataAccess.Entity.RemoveEntityFromEntityGroup(endpointGroup.ID, oldListElement.ID, txn, conn);
                        }
                    }
                    /// Add all items from the new list
                    if (endpointGroup.Endpoints != null)
                    {
                        foreach (DTO.EndpointListElement newListElement in endpointGroup.Endpoints)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(endpointGroup.ID, newListElement.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return endpointGroup;
        }

        public static DTO.EndpointGroup GetEndpointGroup(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            DTO.EndpointGroup endpointGroup = new DTO.EndpointGroup();
            DTO.EntityGroup entityGroup = new DTO.EntityGroup();

            entityGroup = DataAccess.Entity.GetEntityGroup(ID, txn, conn);

            endpointGroup.ID = entityGroup.ID;
            endpointGroup.Name = entityGroup.Name;
            endpointGroup.Description = entityGroup.Description;

            endpointGroup.Endpoints = ListEndpointsByGroup(ID, txn, conn);

            return endpointGroup;
        }

        #endregion

        #region UDDI related functions

        public static List<DTO.EndpointAssociatedUddi> ListEndpointUddiAssociation(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Endpoint.ListEndpointUddiAssociation(ID, txn, conn);
        }

        public static void PublishRuntimeServerEndPointsToUddi(Guid runtimeServerID, bool refresh, SqlTransaction txn, SqlConnection conn)
        {
            if (Configurator.svcConfig.serviceCatalogUddi == null ||
                Configurator.svcConfig.serviceCatalogUddi.UddiIntegrationMode == false)
                return;

            DTO.RuntimeServer runtimeServer = RuntimeServerManager.GetRuntimeServer(runtimeServerID, false, txn, conn);

            if (runtimeServer.Endpoints != null && runtimeServer.Endpoints.Count > 0)
            {
                foreach (DTO.Endpoint endpint in runtimeServer.Endpoints)
                {
                    DataAccess.Endpoint.PublishEndPointToUddi(endpint, runtimeServer, refresh, txn,conn);
                }
            }
        }

        public static void PublishEndPointToUddi(Guid endpointID, Guid runtimeServerID, bool refresh, SqlTransaction txn, SqlConnection conn)
        {
            if (Configurator.svcConfig.serviceCatalogUddi == null ||
                Configurator.svcConfig.serviceCatalogUddi.UddiIntegrationMode == false)
                return;

            DTO.Endpoint endPoint = GetEndpoint(endpointID, runtimeServerID, false, txn, conn);
            DTO.RuntimeServer runtimeServer = RuntimeServerManager.GetRuntimeServer(runtimeServerID, false, txn, conn);
            DataAccess.Endpoint.PublishEndPointToUddi(endPoint, runtimeServer, refresh, txn, conn);

        }


        public static void RemoveRuntimeServerEndPointsFromUddi(Guid runtimeServerID, SqlTransaction txn, SqlConnection conn)
        {
            if (Configurator.svcConfig.serviceCatalogUddi == null ||
                Configurator.svcConfig.serviceCatalogUddi.UddiIntegrationMode == false)
                return;

            DTO.RuntimeServer runtimeServer = RuntimeServerManager.GetRuntimeServer(runtimeServerID, false, txn, conn);

            if (runtimeServer.Endpoints != null && runtimeServer.Endpoints.Count > 0)
            {
                foreach (DTO.Endpoint endpint in runtimeServer.Endpoints)
                {
                    DataAccess.Endpoint.RemoveEndPointFromUddi(endpint, runtimeServer, txn, conn);
                }
            }
        }


        public static void RemoveEndPointFromUddi(Guid endpointID, Guid runtimeServerID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            bool newTransaction = false;

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                if (txn == null)
                {
                    txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);
                    newTransaction = true;
                }

                DTO.Endpoint endPoint = EndpointManager.GetEndpoint(endpointID, runtimeServerID, false, txn, conn);

                DTO.RuntimeServer runtimeServer = RuntimeServerManager.GetRuntimeServer(runtimeServerID, false, txn, conn);

                DataAccess.Endpoint.RemoveEndPointFromUddi(endPoint, runtimeServer, txn, conn);

                if ((autoclose) && (txn != null) && (newTransaction)) txn.Commit();
            }
            catch (Exception)
            {
                if ((txn != null) && (newTransaction)) txn.Rollback();
                throw;
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close();
            }
        }

        #endregion

        #region Private Functions
        /// <summary>
        /// Validates the Schema content for the Endpoint WSDL
        /// </summary>
        /// <param name="endpoint"></param>
        /// <param name="txn"></param>
        /// <param name="conn"></param>
        /// <returns></returns>
        public static bool ValidateEndPointWSDLSchemaContent(DTO.Endpoint endpoint, SqlTransaction txn, SqlConnection conn)
        {
            bool validSchemaContent = true;
            try
            {
                DTO.OperationVersionList publishedOpVers = EndpointManager.ListPublishedOperationsForEndpoint(endpoint.ID, txn, conn);
                if (publishedOpVers.OperationVersions != null && publishedOpVers.OperationVersions.Count > 0)
                {
                    // Load all the operation versions
                    List<DTO.OperationVersion> operationVersions = OperationManager.LoadAllOperationVersions(publishedOpVers, txn, conn);

                    // Load all the relevant schmeas objects
                    Dictionary<string, XmlSchema> schemaHash = SchemaManager.ExtractRelevantSchemaTypes(operationVersions, txn, conn);

                    StringBuilder sbErrors = new StringBuilder();
                    XmlSchemaSet xss = new XmlSchemaSet();
                    foreach (XmlSchema xs in schemaHash.Values)
                        xss.Add(xs);

                    xss.ValidationEventHandler += new ValidationEventHandler
                    (
                        delegate(object ob, ValidationEventArgs args)
                        {
                            try
                            {
                                sbErrors.AppendFormat("SchemaSet Validation Error: Exception[{0}] Message[{1}]", TraceHelper.GetExceptionDetails(args.Exception), args.Message);
                            }
                            catch (Exception ex)
                            {
                                sbErrors.AppendFormat("Failed to Validate SchemaSet due to error [{0}]", TraceHelper.GetExceptionDetails(ex));
                            }
                        }
                    );
                    xss.Compile();
                    if (sbErrors.ToString().Length > 0)
                    {
                        throw new ApplicationException(string.Format("Failed to create/compile SchemSet due to error [{0}]", sbErrors.ToString()));
                    }
                }
            }
            catch (Exception ex)
            {
                TraceHelper.TraceWarning("Failed to validate WSDL Schema content for Endpoint [{0}] due to error [{1}]", endpoint.Name, TraceHelper.GetExceptionDetails(ex));
                validSchemaContent = false;
            }
            return validSchemaContent;
        }

        /// <summary>
        /// Creates a WSDL from the metadata information stored in the service catalog
        /// </summary>
        /// <param name="endpoint"></param>
        /// <param name="txn"></param>
        /// <param name="conn"></param>
        /// <returns></returns>
        private static XmlElement CreateWSDLFromEndpointMetadata(DTO.Endpoint endpoint, SqlTransaction txn, SqlConnection conn)
        {
            XmlElement wsdlElement = null;
            DTO.OperationVersionList publishedOpVers = EndpointManager.ListPublishedOperationsForEndpoint(endpoint.ID, txn, conn);
            if (publishedOpVers.OperationVersions != null && publishedOpVers.OperationVersions.Count > 0)
            {
                // Load all the operation versions
                List<DTO.OperationVersion> operationVersions = OperationManager.LoadAllOperationVersions(publishedOpVers, txn, conn);

                // Load all the relevant schmeas objects
                Dictionary<string, XmlSchema> schemaHash = SchemaManager.ExtractRelevantSchemaTypes(operationVersions, txn, conn);

                WCF.Binding mseBinding = null;
                WSDL.ServiceDescription serviceDescription = null;

                string virtualServiceName = "", serviceNamespace = "";
                Uri serviceUri = null;
                serviceNamespace = CreateServiceInformation(endpoint, ref virtualServiceName, ref serviceUri, ref mseBinding);

                // Create the wsdl:types section of the WSDL from schemas in the repository
                serviceDescription = CreateServiceDesciptionFromSchemas(schemaHash, operationVersions, serviceNamespace, txn, conn);

                // Create the wsdl:message, wsdl:portTypes and wsdl:port sections of the WSDL
                AppendContractToServiceDescription(serviceDescription, endpoint, serviceUri, publishedOpVers, mseBinding, serviceNamespace,txn, conn);

                // Convert the WSDL into an XML documnet ans return the top level element
                XmlDocument metadataDocument = new XmlDocument();
                string metadataXml = CatalogSvcHelper.ConvertWSDLToString(serviceDescription);
                metadataDocument.LoadXml(metadataXml.ToString());
                wsdlElement = metadataDocument.DocumentElement;
            }
            return wsdlElement;
        }

        private static void AppendContractToServiceDescription(WSDL.ServiceDescription serviceDescription, DTO.Endpoint endpoint, Uri serviceUri, DTO.OperationVersionList publishedOpVers,
                                                                WCF.Binding mseBinding, string serviceNamespaceUri, SqlTransaction txn, SqlConnection conn)
        {

            // Create the wsdl:portType section of the WSDL
            WSDL.PortType portType = new System.Web.Services.Description.PortType();
            portType.Name = endpoint.ServiceName;
            serviceDescription.PortTypes.Add(portType);
            WSDL.Binding binding = null;
            XmlDocument xDoc = new XmlDocument();

            // Create this section only if Soap11 & Soap12
            if (mseBinding.MessageVersion.Envelope != EnvelopeVersion.None)
            {
                // Create the Binding
                binding = new System.Web.Services.Description.Binding();
                binding.Name = string.Format("{0}Soap", endpoint.ServiceName);
                binding.Type = new XmlQualifiedName(string.Format("tns:{0}Soap", endpoint.ServiceName));

                if (mseBinding.MessageVersion.Envelope == EnvelopeVersion.Soap11)
                {
                    WSDL.SoapBinding soapBinding = new WSDL.SoapBinding();
                    soapBinding.Transport = WSDL.SoapBinding.HttpTransport;
                    binding.Extensions.Add(soapBinding);
                }
                else
                {
                    WSDL.Soap12Binding soapBinding = new WSDL.Soap12Binding();
                    soapBinding.Transport = WSDL.Soap12Binding.HttpTransport;
                    binding.Extensions.Add(soapBinding);
                }
                serviceDescription.Bindings.Add(binding);
            }

            foreach (DTO.OperationVersionListElement element in publishedOpVers.OperationVersions)
            {
                DTO.OperationVersion opVersion = OperationManager.GetOperationVersion(element.ID, txn, conn);
                string opName = opVersion.OperationName;

                WSDL.Operation operation = new System.Web.Services.Description.Operation();
                operation.Name = opName;
                portType.Operations.Add(operation);

                WSDL.OperationBinding operationbinding = null;
                // Create this section only if Soap11 & Soap12
                if (mseBinding.MessageVersion.Envelope != EnvelopeVersion.None)
                {
                    operationbinding = new System.Web.Services.Description.OperationBinding();
                    operationbinding.Name = opName;
                }

                if (opVersion.RequestMsg != null && opVersion.RequestMsg.Count > 0)
                {
                    WSDL.Message message = new System.Web.Services.Description.Message();
                    message.Name = string.Format("{0}SoapIn", opName);
                    int counter = 0;
                    foreach (DTO.MessagePart part in opVersion.RequestMsg)
                    {
                        WSDL.MessagePart messagePart = new System.Web.Services.Description.MessagePart();
                        messagePart.Element = new XmlQualifiedName(part.SchemaObject.Name, part.SchemaObject.NamespaceUri);
                        messagePart.Name = string.Format("{0}RequestPart{1}", part.SchemaObject.Name, counter);
                        //messagePart.Name = "parameters";
                        message.Parts.Add(messagePart);
                        counter++;
                    }
                    serviceDescription.Messages.Add(message);

                    WSDL.OperationInput operationInput = new WSDL.OperationInput();
                    operationInput.Message = new XmlQualifiedName("tns:" + message.Name);
                    operation.Messages.Add(operationInput);

                    // Create this section only if Soap11 & Soap12
                    if (mseBinding.MessageVersion.Envelope != EnvelopeVersion.None)
                    {
                        WSDL.InputBinding inputBinding = new System.Web.Services.Description.InputBinding();
                        operationbinding.Input = inputBinding;
                        if (mseBinding.MessageVersion.Envelope == EnvelopeVersion.Soap11)
                        {
                            WSDL.SoapBodyBinding soapBodyBinding = new System.Web.Services.Description.SoapBodyBinding();
                            soapBodyBinding.Use = System.Web.Services.Description.SoapBindingUse.Literal;
                            inputBinding.Extensions.Add(soapBodyBinding);
                        }
                        else
                        {
                            WSDL.Soap12BodyBinding soapBodyBinding = new System.Web.Services.Description.Soap12BodyBinding();
                            soapBodyBinding.Use = System.Web.Services.Description.SoapBindingUse.Literal;
                            inputBinding.Extensions.Add(soapBodyBinding);
                        }
                        // Add Header Message and Binding Information
                        if (opVersion.RequestHeaders != null && opVersion.RequestHeaders.Count > 0)
                        {
                            foreach (DTO.MessageHeader header in opVersion.RequestHeaders)
                            {
                                WSDL.Message headerMessage = new WSDL.Message();
                                headerMessage.Name = string.Format("RequestHeader_{0}{1}", opVersion.OperationName, header.Name);
                                WSDL.MessagePart headerMessagePart = new WSDL.MessagePart();
                                headerMessagePart.Element = new XmlQualifiedName(header.SchemaObject.Name, header.SchemaObject.NamespaceUri);
                                headerMessagePart.Name = string.Format("RequestHeaderPart_{0}", header.Name);
                                headerMessage.Parts.Add(headerMessagePart);
                                serviceDescription.Messages.Add(headerMessage);

                                WSDL.SoapHeaderBinding soapHeaderBinding = null;
                                if (mseBinding.MessageVersion.Envelope == EnvelopeVersion.Soap11)
                                {
                                    soapHeaderBinding = new System.Web.Services.Description.SoapHeaderBinding();
                                }
                                else
                                {
                                    soapHeaderBinding = new System.Web.Services.Description.Soap12HeaderBinding();
                                }
                                soapHeaderBinding.Message = new XmlQualifiedName(headerMessage.Name, serviceNamespaceUri);
                                soapHeaderBinding.Part = headerMessagePart.Name;
                                soapHeaderBinding.Use = System.Web.Services.Description.SoapBindingUse.Literal;
                                inputBinding.Extensions.Add(soapHeaderBinding);
                            }
                        }
                    }

                }
                if (opVersion.ResponseMsg != null && opVersion.ResponseMsg.Count > 0)
                {
                    WSDL.Message message = new System.Web.Services.Description.Message();
                    message.Name = string.Format("{0}SoapOut", opName);
                    int counter = 0;
                    foreach (DTO.MessagePart part in opVersion.ResponseMsg)
                    {
                        WSDL.MessagePart messagePart = new System.Web.Services.Description.MessagePart();
                        messagePart.Element = new XmlQualifiedName(part.SchemaObject.Name, part.SchemaObject.NamespaceUri);
                        messagePart.Name = string.Format("{0}ResponsePart{1}", part.SchemaObject.Name, counter);
                        //messagePart.Name = "parameters";
                        message.Parts.Add(messagePart);
                        counter++;
                    }
                    serviceDescription.Messages.Add(message);

                    WSDL.OperationOutput operationOutput = new WSDL.OperationOutput();
                    operationOutput.Message = new XmlQualifiedName("tns:" + message.Name);
                    operation.Messages.Add(operationOutput);

                    // Create this section only if Soap11 & Soap12
                    if (mseBinding.MessageVersion.Envelope != EnvelopeVersion.None)
                    {
                        WSDL.OutputBinding outputBinding = new System.Web.Services.Description.OutputBinding();
                        operationbinding.Output = outputBinding;
                        if (mseBinding.MessageVersion.Envelope == EnvelopeVersion.Soap11)
                        {
                            WSDL.SoapBodyBinding soapBodyBinding = new System.Web.Services.Description.SoapBodyBinding();
                            soapBodyBinding.Use = System.Web.Services.Description.SoapBindingUse.Literal;
                            outputBinding.Extensions.Add(soapBodyBinding);
                        }
                        else
                        {
                            WSDL.Soap12BodyBinding soapBodyBinding = new System.Web.Services.Description.Soap12BodyBinding();
                            soapBodyBinding.Use = System.Web.Services.Description.SoapBindingUse.Literal;
                            outputBinding.Extensions.Add(soapBodyBinding);
                        }
                        // Add Header Information
                        if (opVersion.ResponseHeaders != null && opVersion.ResponseHeaders.Count > 0)
                        {
                            foreach (DTO.MessageHeader header in opVersion.ResponseHeaders)
                            {
                                WSDL.Message headerMessage = new WSDL.Message();
                                headerMessage.Name = string.Format("ResponseHeader_{0}{1}", opVersion.OperationName, header.Name);
                                WSDL.MessagePart headerMessagePart = new WSDL.MessagePart();
                                headerMessagePart.Element = new XmlQualifiedName(header.SchemaObject.Name, header.SchemaObject.NamespaceUri);
                                headerMessagePart.Name = string.Format("ResponseHeaderPart_{0}", message.Name);
                                headerMessage.Parts.Add(headerMessagePart);
                                serviceDescription.Messages.Add(headerMessage);

                                WSDL.SoapHeaderBinding soapHeaderBinding = null;
                                if (mseBinding.MessageVersion.Envelope == EnvelopeVersion.Soap11)
                                {
                                    soapHeaderBinding = new System.Web.Services.Description.SoapHeaderBinding();
                                }
                                else
                                {
                                    soapHeaderBinding = new System.Web.Services.Description.Soap12HeaderBinding();
                                }
                                soapHeaderBinding.Message = new XmlQualifiedName(headerMessage.Name, serviceNamespaceUri);
                                soapHeaderBinding.Part = headerMessagePart.Name;
                                soapHeaderBinding.Use = System.Web.Services.Description.SoapBindingUse.Literal;
                                outputBinding.Extensions.Add(soapHeaderBinding);
                            }
                        }
                    }
                }

                // Create this section only if Soap11 & Soap12
                if (mseBinding.MessageVersion.Envelope != EnvelopeVersion.None)
                {
                    WSDL.SoapOperationBinding soapOperationBinding = new WSDL.SoapOperationBinding();
                    soapOperationBinding.Style = WSDL.SoapBindingStyle.Document;
                    soapOperationBinding.SoapAction = opName;
                    operationbinding.Extensions.Add(soapOperationBinding);
                }
                if (opVersion.SoapFaults != null && opVersion.SoapFaults.Count > 0)
                {
                    foreach (DTO.SoapFault fault in opVersion.SoapFaults)
                    {
                        WSDL.Message message = new System.Web.Services.Description.Message();
                        message.Name = string.Format("{0}_{1}_FaultMessage", opName, fault.Name);

                        WSDL.MessagePart messagePart = new System.Web.Services.Description.MessagePart();
                        messagePart.Element = new XmlQualifiedName(fault.SchemaObject.Name, fault.SchemaObject.NamespaceUri);
                        messagePart.Name = "detail";

                        message.Parts.Add(messagePart);
                        serviceDescription.Messages.Add(message);

                        WSDL.OperationFault operationFault = new WSDL.OperationFault();
                        operationFault.Name = fault.Name;
                        operationFault.Message = new XmlQualifiedName("tns:" + message.Name);
                        // Create the WSAddressing's Action attribute
                        XmlAttribute action = xDoc.CreateAttribute("wsaw", "Action", "http://www.w3.org/2006/05/addressing/wsdl");
                        action.Value = fault.Action;
                        operationFault.ExtensibleAttributes = new XmlAttribute[] { action };
                        operation.Faults.Add(operationFault);

                        // Create this section only if Soap11 & Soap12
                        if (mseBinding.MessageVersion.Envelope != EnvelopeVersion.None)
                        {
                            WSDL.FaultBinding faultBinding = new WSDL.FaultBinding();
                            faultBinding.Name = fault.Name;
                            if (mseBinding.MessageVersion.Envelope == EnvelopeVersion.Soap11)
                            {
                                WSDL.SoapFaultBinding soapFaultBinding = new WSDL.SoapFaultBinding();
                                soapFaultBinding.Name = fault.Name;
                                soapFaultBinding.Namespace = null;
                                soapFaultBinding.Use = System.Web.Services.Description.SoapBindingUse.Literal;
                                faultBinding.Extensions.Add(soapFaultBinding);
                            }
                            else
                            {
                                WSDL.Soap12FaultBinding soapFaultBinding = new WSDL.Soap12FaultBinding();
                                soapFaultBinding.Name = fault.Name;
                                soapFaultBinding.Namespace = null;
                                soapFaultBinding.Use = System.Web.Services.Description.SoapBindingUse.Literal;
                                faultBinding.Extensions.Add(soapFaultBinding);
                            }
                            operationbinding.Faults.Add(faultBinding);
                        }
                    }
                }
                // Create this section only if Soap11 & Soap12
                if (mseBinding.MessageVersion.Envelope != EnvelopeVersion.None)
                {
                    binding.Operations.Add(operationbinding);
                }
            }

            // Create this section only if Soap11 & Soap12
            // This section will be overwritten by the one from the Runtime
            //if (mseBinding.MessageVersion.Envelope != EnvelopeVersion.None)
            //{
            //    WSDL.Service service = new System.Web.Services.Description.Service();
            //    service.Name = endpoint.ServiceName;

            //    WSDL.Port port = new WSDL.Port();
            //    port.Name = string.Format("{0}Soap", endpoint.ServiceName);
            //    port.Binding = new XmlQualifiedName(string.Format("tns:{0}Soap", endpoint.ServiceName));

            //    if (mseBinding.MessageVersion.Envelope == EnvelopeVersion.Soap11)
            //    {
            //        // Create a SoapAddress extensibility element to add to the port.
            //        WSDL.SoapAddressBinding soapAddressBinding = new WSDL.SoapAddressBinding();
            //        soapAddressBinding.Location = serviceUri.ToString();
            //        port.Extensions.Add(soapAddressBinding);
            //    }
            //    else
            //    {
            //        // Create a SoapAddress extensibility element to add to the port.
            //        WSDL.Soap12AddressBinding soapAddressBinding = new WSDL.Soap12AddressBinding();
            //        soapAddressBinding.Location = serviceUri.ToString();
            //        port.Extensions.Add(soapAddressBinding);
            //    }
            //    service.Ports.Add(port);
            //    serviceDescription.Services.Add(service);
            //}
        }

        private static WSDL.ServiceDescription CreateServiceDesciptionFromSchemas(Dictionary<string, XmlSchema> schemaHash, List<DTO.OperationVersion> operationVersions, string serviceNamespace, SqlTransaction txn, SqlConnection conn)
        {

            WSDL.ServiceDescription serviceDescription = new System.Web.Services.Description.ServiceDescription();

            // Validate Schemas
            try
            {
                XmlSchemaSet xss = new XmlSchemaSet();
                foreach (XmlSchema xs in schemaHash.Values)
                {
                    xss.Add(xs);
                }
                xss.Compile();
            }
            catch (Exception ex)
            {
                throw new ApplicationException(string.Format("Failed to validate schemas as schema set compilation failed due to error [{0}]", CatalogSvcHelper.GetExceptionDetails(ex)));
            }

            foreach (XmlSchema xs in schemaHash.Values)
            {
                serviceDescription.Types.Schemas.Add(xs);
            }

            // We need to assign a targetnamespace to the WSDL
            // Assigning it using the object model i.e. serviceDescription.TargetNamespace cause the WSDL to loose all the namespaces 
            // that it has while we assigned types (schemas) to it
            string data = CatalogSvcHelper.ConvertWSDLToString(serviceDescription);

            XmlDocument xdoc = new XmlDocument();
            xdoc.LoadXml(data);
            XmlAttribute xat = xdoc.CreateAttribute("xmlns:tns");
            xat.Value = serviceNamespace;
            xdoc.DocumentElement.Attributes.Append(xat);

            xat = xdoc.CreateAttribute("targetNamespace");
            xat.Value = serviceNamespace;
            xdoc.DocumentElement.Attributes.Append(xat);

            serviceDescription = WSDL.ServiceDescription.Read(new StringReader(xdoc.OuterXml));
            return serviceDescription;
        }

        private static string CreateServiceInformation(DTO.Endpoint endpoint, ref string virtualServiceName, ref Uri serviceUri, ref WCF.Binding mseBinding)
        {
            string virtualServiceNS = "";

            mseBinding = BindingHelper.DeserializeBinding(endpoint.Binding.BindingConfig);
            try
            {
                serviceUri = new Uri(mseBinding.Scheme.ToString() + "://" + endpoint.Address);
            }
            catch (Exception ex)
            {
                throw new ApplicationException(string.Format("Failed to create service Uri due to error. Endpoint address may be invalid [{0}]", endpoint.Address), ex);
            }
            if (string.IsNullOrEmpty(endpoint.NamespaceURI) == true)
            {
                if (string.IsNullOrEmpty(endpoint.ServiceName) == false)
                    virtualServiceName = endpoint.ServiceName;
                else
                {
                    double number;
                    if (Double.TryParse(Convert.ToString(serviceUri.DnsSafeHost.Substring(0, 1), CultureInfo.InvariantCulture), System.Globalization.NumberStyles.Any, NumberFormatInfo.InvariantInfo, out number))
                        virtualServiceName = "WS" + serviceUri.DnsSafeHost;
                    else
                        virtualServiceName = serviceUri.DnsSafeHost;
                }
                virtualServiceNS = MseConstants.MSENamespace + "/" + endpoint.Binding.Name + "/" + virtualServiceName;
            }
            else
            {
                virtualServiceNS = endpoint.NamespaceURI;
            }
            return virtualServiceNS;
        }
       
        private static string FullSoapAction(DTO.OperationVersion version)
        {
            string response = string.Empty;

            if (version == null) throw new ArgumentNullException("version");

            if ((!string.IsNullOrEmpty(version.Version.Qfe)) && (int.Parse(version.Version.Qfe, nfi) != 0))
            {
                response = string.Format(nfi, "{0}:{1}.{2}.{3}.{4}", version.OperationName, version.Version.Major, version.Version.Minor, version.Version.Build, version.Version.Qfe);
            }
            else
            {
                if ((!string.IsNullOrEmpty(version.Version.Build)) && (int.Parse(version.Version.Build, nfi) != 0))
                {
                    response = string.Format(nfi, "{0}:{1}.{2}.{3}", version.OperationName, version.Version.Major, version.Version.Minor, version.Version.Build);
                }
                else
                {
                    response = string.Format(nfi, "{0}:{1}.{2}", version.OperationName, version.Version.Major, version.Version.Minor);
                }
            }

            return response;
        }

        #endregion
    }
}
